<%#
kind: snippet
name: disk_enc_clevis_tang
model: ProvisioningTemplate
snippet: true
description: |
  Binds encrypted root directory ('/') utilizing Clevis to Tang server(s) for
  decryption. All parent devices containing a LUKS container will be used.
  The temporary passphrase will be removed afterwards. Currently, only Red Hat
  family and Ubuntu operating systems are supported.
-%>
<%
  passphrase = host_param('disk_enc_passphrase', 'linux')
  tang_server_list = []
  packages_redhat = "clevis clevis-luks clevis-systemd clevis-dracut"
  packages_ubuntu = "clevis clevis-luks clevis-systemd clevis-initramfs"

  unless host_param('disk_enc_tang_servers').blank?
    if host_param('disk_enc_tang_servers').is_a?(String)
      tang_server_list = [host_param('disk_enc_tang_servers')]
    else
      tang_server_list = host_param('disk_enc_tang_servers')
    end
  end
-%>

<% if (@host.operatingsystem.family == 'Redhat' || @host.operatingsystem.name == 'Ubuntu') && tang_server_list.present? -%>

cat > /tmp/rootdir-luks-device.sh << "EOF"
#!/bin/bash
#
# SPDX-FileCopyrightText: 2024 Jan Löser <loeser@atix.de>
# SPDX-FileCopyrightText: 2024 Martin Spiessl <spiessl@atix.de>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This script tries to find all LUKS devices mounted in /etc/crypttab.
#
set -o pipefail

mapfile -t maybe_luks_devices < <(
    awk '{print $1}' /etc/crypttab | \
    xargs -rn1 find /dev -name | \
    xargs -rn1 readlink -f
)
luks_devices=()
while [[ ${#maybe_luks_devices[@]} -gt 0 ]] ; do
    # classify maybe_luks_devices into luks_devices and non_luks_devices
    non_luks_devices=()
    for dev in "${maybe_luks_devices[@]}" ; do
        /sbin/cryptsetup luksDump "$dev" &>/dev/null && luks_devices+=("$dev") || non_luks_devices+=("$dev")
    done
    # resolve non_luks_devices to discover new maybe_luks_devices
    set -e
    maybe_luks_devices=()
    for dev in "${non_luks_devices[@]}" ; do
        mapfile -t  slaves < <(find "/sys/class/block/$(basename "$dev")/slaves" -type l)
        for slave in "${slaves[@]}" ; do
            slavedev=$(find /dev -name "$(basename "$slave")" | head -n1)
            maybe_luks_devices+=("$slavedev")
        done
    done
    set +e
done
printf "%s\n" "${luks_devices[@]}"
EOF

# needs bash here because Ubuntu's sh (dash) doesn't support `-o pipefail` option
luksdevs=$(bash /tmp/rootdir-luks-device.sh)
if [ -n "$luksdevs" ]; then
  echo "LUKS devices found for '/': $luksdevs"

<% if @host.operatingsystem.family == 'Redhat' -%>
  $PKG_MANAGER_INSTALL <%= packages_redhat %>
<% elsif @host.operatingsystem.name == 'Ubuntu' -%>
  $PKG_MANAGER_INSTALL <%= packages_ubuntu %>
<% end -%>

  for luksdev in $luksdevs; do
  <% for tang_server in tang_server_list -%>
    echo '<%= passphrase %>' | clevis luks bind -y -k - -d $luksdev tang '{"url": "<%= tang_server %>"}'
    if [ $? -ne 0 ]; then
      echo "---"
      echo "There was an error during Clevis LUKS bind of '$luksdev' to Tang server '<%= tang_server %>'."
      echo "System halted."
      sleep infinity
    fi
  <% end -%>
    echo '<%= passphrase %>' | cryptsetup luksRemoveKey $luksdev
  done
  systemctl enable clevis-luks-askpass.path
  systemctl enable remote-cryptsetup.target

<% if @host.operatingsystem.family == 'Redhat' -%>
  dracut --verbose --force --hostonly-cmdline --regenerate-all
<% elsif @host.operatingsystem.name == 'Ubuntu' -%>
  update-initramfs -u -k 'all'
<% end -%>

else
  echo "No LUKS device found!"
fi

<% end -%>
